

const vs_source = `
attribute vec4 a_Position;
attribute vec4 a_Color;
attribute vec4 a_Normal;
uniform mat4 u_ModelMatrix; //模型矩阵
uniform mat4 u_MvpMatrix;
uniform mat4 u_NormalMatrix;
uniform vec3 u_LightColor; //光的颜色
uniform vec3 u_LightDirection;
uniform vec3 u_LightPosition;   // 点光源
uniform vec3 u_AmbientColor;   // 环境光颜色
uniform vec3 u_AmbientLight;    //环境光颜色
varying vec4 v_Color;
void main(){
    gl_Position = u_MvpMatrix * a_Position;
    vec4 normal = u_NormalMatrix * a_Normal;
    vec4 vertexPosition = u_ModelMatrix * a_Position;
    vec3 lightDirection = normalize(u_LightPosition - vec3(vertexPosition));
    float nDotL1 = max(dot(lightDirection, normalize(normal.xyz)), 0.0);
    vec3 diffuse = u_LightColor  * a_Color.rgb *  nDotL1;  
    vec3 ambient = u_AmbientLight * u_AmbientColor.rgb;
    float nDotL = max(dot(u_LightDirection, normalize(normal.xyz)), 0.0);
    vec3 colorbase = vec3(a_Color.xyz * nDotL);
    v_Color = vec4(diffuse + ambient + colorbase,a_Color.a);
}`;


const fs_source = `
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_Color;
void main(){
    gl_FragColor = v_Color;
}`;


function main() {

    let canvas = document.getElementById("glCanvas");
    const gl = canvas.getContext('webgl');
    if (!gl) {
        console.log("WebGL Failed");
        return;
    }
    console.log("width", canvas.width)
    console.log("height", canvas.height)
    gl.viewport(0, 0, canvas.width, canvas.height)

    if (!initShaders(gl, vs_source, fs_source)) {
        console.error("Failed init Shader");
        return;
    }

    //
    var n = initVertexBuffers(gl);
    if (n < 0) {
        console.log('Failed to set the vertex information');
        return;
    }

    // Set the clear color and enable the depth test
    gl.clearColor(0, 0, 0, 1);
    gl.enable(gl.DEPTH_TEST);

    var u_ModelMatrix = gl.getUniformLocation(gl.program,'u_ModelMatrix');
    var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
    var u_NormalMatrix = gl.getUniformLocation(gl.program, 'u_NormalMatrix');
    var u_LightDirection = gl.getUniformLocation(gl.program, 'u_LightDirection');
    var u_LightPosition  = gl.getUniformLocation(gl.program, 'u_LightPosition');
    var u_LightColor = gl.getUniformLocation(gl.program,'u_LightColor');
    var u_AmbientLight = gl.getUniformLocation(gl.program,'u_AmbientLight');
    let u_AmbientColor = gl.getUniformLocation(gl.program,'u_AmbientColor');

    if (!u_MvpMatrix || !u_NormalMatrix || !u_LightDirection || !u_LightPosition || !u_LightColor || !u_ModelMatrix) { 
        console.log('Failed to get the storage location');
        return;
    }

    var vpMatrix = new Matrix4();   // View projection matrix
    // Calculate the view projection matrix
    vpMatrix.setPerspective(30, canvas.width/canvas.height, 1, 100);
    vpMatrix.lookAt(3, 3, 7, 0, 0, 0, 0, 1, 0);


    // Set the light direction (in the world coordinate)

    var lightDirection = new Vector3([0.5, 3.0, 4.0]);
    lightDirection.normalize();     // Normalize
    gl.uniform3fv(u_LightDirection, lightDirection.elements);
    
    // 设置个点光源
    gl.uniform3f(u_LightColor,1.0,1.0,1.0);
    gl.uniform3f(u_LightPosition,1.0,15.0,15.0);

    // 环境光
    gl.uniform3f(u_AmbientColor,0.2,0.2,0.2);
    gl.uniform3f(u_AmbientLight,0.5,0.5,0.5)
    



    var currentAngle = 0.0;  // Current rotation angle
    var modelMatrix = new Matrix4();  // Model matrix
    var mvpMatrix = new Matrix4();    // Model view projection matrix
    var normalMatrix = new Matrix4(); // Transformation matrix for normals

    var tick = function() {
        currentAngle = animate(currentAngle);  // Update the rotation angle
    
        // Calculate the model matrix
        modelMatrix.setRotate(currentAngle, 0, 1, 0); // Rotate around the y-axis
        gl.uniformMatrix4fv(u_ModelMatrix, false, mvpMatrix.elements);

        mvpMatrix.set(vpMatrix).multiply(modelMatrix);
        gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
    
        // Pass the matrix to transform the normal based on the model matrix to u_NormalMatrix
        normalMatrix.setInverseOf(modelMatrix);
        normalMatrix.transpose();
        gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
    
        // Clear color and depth buffer
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    
        // Draw the cube
        gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
    
        requestAnimationFrame(tick, canvas); // Request that the browser ?calls tick
    };
    tick();
}

function initVertexBuffers(gl){

    var vertices = new Float32Array([
        1.0, 1.0, 1.0,  -1.0, 1.0, 1.0,  -1.0,-1.0, 1.0,   1.0,-1.0, 1.0, // v0-v1-v2-v3 front
        1.0, 1.0, 1.0,   1.0,-1.0, 1.0,   1.0,-1.0,-1.0,   1.0, 1.0,-1.0, // v0-v3-v4-v5 right
        1.0, 1.0, 1.0,   1.0, 1.0,-1.0,  -1.0, 1.0,-1.0,  -1.0, 1.0, 1.0, // v0-v5-v6-v1 up
       -1.0, 1.0, 1.0,  -1.0, 1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0,-1.0, 1.0, // v1-v6-v7-v2 left
       -1.0,-1.0,-1.0,   1.0,-1.0,-1.0,   1.0,-1.0, 1.0,  -1.0,-1.0, 1.0, // v7-v4-v3-v2 down
        1.0,-1.0,-1.0,  -1.0,-1.0,-1.0,  -1.0, 1.0,-1.0,   1.0, 1.0,-1.0  // v4-v7-v6-v5 back
     ]);
   
     // Colors
     var colors = new Float32Array([
       1, 0, 0,   1, 0, 0,   1, 0, 0,  1, 0, 0,     // v0-v1-v2-v3 front
       0, 1, 0,   0, 1, 0,   0, 1, 0,  0, 1, 0,     // v0-v3-v4-v5 right
       0, 0, 1,   0, 0, 1,   0, 0, 1,  0, 0, 1,     // v0-v5-v6-v1 up
       1, 1, 0,   1, 1, 0,   1, 1, 0,  1, 1, 0,   // v1-v6-v7-v2 left
       1, 0, 1,   1, 0, 1,   1, 0, 1,  1, 0, 1,     // v7-v4-v3-v2 down
       0, 1, 1,   0, 1, 1,   0, 1, 1,  0, 1, 1,　    // v4-v7-v6-v5 back
    ]);
   
     // Normal
     var normals = new Float32Array([
       0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,   0.0, 0.0, 1.0,  // v0-v1-v2-v3 front
       1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,   1.0, 0.0, 0.0,  // v0-v3-v4-v5 right
       0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,   0.0, 1.0, 0.0,  // v0-v5-v6-v1 up
      -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  -1.0, 0.0, 0.0,  // v1-v6-v7-v2 left
       0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,   0.0,-1.0, 0.0,  // v7-v4-v3-v2 down
       0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0,   0.0, 0.0,-1.0   // v4-v7-v6-v5 back
     ]);
   
     // Indices of the vertices
     var indices = new Uint8Array([
        0, 1, 2,   0, 2, 3,    // front
        4, 5, 6,   4, 6, 7,    // right
        8, 9,10,   8,10,11,    // up
       12,13,14,  12,14,15,    // left
       16,17,18,  16,18,19,    // down
       20,21,22,  20,22,23     // back
    ]);
   
     // Write the vertex property to buffers (coordinates, colors and normals)
     if (!initArrayBuffer(gl, 'a_Position', vertices, 3, gl.FLOAT)) return -1;
     if (!initArrayBuffer(gl, 'a_Color', colors, 3, gl.FLOAT)) return -1;
     if (!initArrayBuffer(gl, 'a_Normal', normals, 3, gl.FLOAT)) return -1;
   
     // Unbind the buffer object
     gl.bindBuffer(gl.ARRAY_BUFFER, null);
   
     // Write the indices to the buffer object
     var indexBuffer = gl.createBuffer();
     if (!indexBuffer) {
       console.log('Failed to create the buffer object');
       return false;
     }
     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
     gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
   
     return indices.length;
}

function initArrayBuffer(gl, attribute, data, num, type) {
    // Create a buffer object
    var buffer = gl.createBuffer();
    if (!buffer) {
      console.log('Failed to create the buffer object');
      return false;
    }
    // Write date into the buffer object
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
    // Assign the buffer object to the attribute variable
    var a_attribute = gl.getAttribLocation(gl.program, attribute);
    if (a_attribute < 0) {
      console.log('Failed to get the storage location of ' + attribute);
      return false;
    }
    gl.vertexAttribPointer(a_attribute, num, type, false, 0, 0);
    // Enable the assignment of the buffer object to the attribute variable
    gl.enableVertexAttribArray(a_attribute);
  
    return true;
}


// Rotation angle (degrees/second)
var ANGLE_STEP = 30.0;
// Last time that this function was called
var g_last = Date.now();
function animate(angle) {
  // Calculate the elapsed time
  var now = Date.now();
  var elapsed = now - g_last;
  g_last = now;
  // Update the current rotation angle (adjusted by the elapsed time)
  var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
  return newAngle %= 360;
}